# Klipper build system
#
# Copyright (C) 2016-2020  Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.

# 输出目录
OUT=out/

# TOOLCHAIN_DIR = /home/chen/tools/soft/STM32CubeIde/plugins/com.st.stm32cube.ide.mcu.externaltools.gnu-tools-for-stm32.12.3.rel1.linux64_1.0.100.202403111256/tools/bin/
TOOLCHAIN_DIR = 
# 编译过程中使用的配置文件，由menuconfig生成
# export 关键字用于将变量作用于扩展到所有的子Makefile中
export KCONFIG_CONFIG     := $(CURDIR)/.config
# -include表示在包含文件时如果出现文件不存在或者读取失败的情况则直接忽略
-include $(KCONFIG_CONFIG)
# 常用命令定义,CROSS_PREFIX会在对应板子的Makefile中配置
CC=$(TOOLCHAIN_DIR)$(CROSS_PREFIX)gcc
AS=$(TOOLCHAIN_DIR)$(CROSS_PREFIX)as
LD=$(TOOLCHAIN_DIR)$(CROSS_PREFIX)ld
OBJCOPY=$(TOOLCHAIN_DIR)$(CROSS_PREFIX)objcopy
OBJDUMP=$(TOOLCHAIN_DIR)$(CROSS_PREFIX)objdump
STRIP=$(TOOLCHAIN_DIR)$(CROSS_PREFIX)strip
CPP=cpp
PYTHON=python3

# 源码文件
src-y =
# 待编译的源码目录
dirs-y = src

# 默认的编译选项
cc-option=$(shell if test -z "`$(1) $(2) -S -o /dev/null -xc /dev/null 2>&1`" \
    ; then echo "$(2)"; else echo "$(3)"; fi ;)

CFLAGS := -I$(OUT) -Isrc -I$(OUT)board-generic/ -std=gnu11 -O2 -MD \
    -Wall -Wold-style-definition $(call cc-option,$(CC),-Wtype-limits,) \
    -ffunction-sections -fdata-sections -fno-delete-null-pointer-checks -fno-exceptions
CFLAGS += -flto -fwhole-program -fno-use-linker-plugin -ggdb3

OBJS_klipper.elf = $(patsubst %.c, $(OUT)src/%.o,$(src-y))
OBJS_klipper.elf += $(OUT)compile_time_request.o
CFLAGS_klipper.elf = $(CFLAGS) -Wl,--gc-sections

CPPFLAGS = -I$(OUT) -P -MD -MT $@

# 默认生成的目标文件
target-y := $(OUT)klipper.elf

all:

# 使用“make V=1”运行打开回显功能以查看实际的编译命令
ifdef V
Q=
else
Q=@
MAKEFLAGS += --no-print-directory
endif

# 包含板子特定的Makefile
include src/Makefile
# CONFIG_BOARD_DIRECTORY在.config中定义，用于指定特定板子使用的特定源码目录
-include src/$(patsubst "%",%,$(CONFIG_BOARD_DIRECTORY))/Makefile

################ 主构建规则

$(OUT)%.o: %.c $(OUT)autoconf.h
	@echo "  Compiling $@"
	$(Q)$(CC) $(CFLAGS) -c $< -o $@

# 生成链接脚本文件，通过通用的*.lds.S文件经过CPP预处理后(宏替换、条件编译等)生成对应平台的链接脚本文件
$(OUT)%.ld: %.lds.S $(OUT)autoconf.h
	@echo "  Preprocessing $@"
	$(Q)$(CPP) -I$(OUT) -P -MD -MT $@ $< -o $@

$(OUT)klipper.elf: $(OBJS_klipper.elf)
	@echo "  Linking $@"
	$(Q)$(CC) $(OBJS_klipper.elf) $(CFLAGS_klipper.elf) -o $@
	$(Q)scripts/check-gcc.sh $@ $(OUT)compile_time_request.o

################ 编译时需要使用的必须文件

# 拷贝%.o文件的.compile_time_request段的数据到对应的%.o.ctr文件中
# .compile_time_request段中的数据是自定义的信息
$(OUT)%.o.ctr: $(OUT)%.o
	$(Q)$(OBJCOPY) -j '.compile_time_request' -O binary $^ $@

#找到out/src/*.o.ctr文件，将里面的内容(结束符替换为换行符)逐行拷贝到compile_time_request.txt文件中
$(OUT)compile_time_request.o: $(patsubst %.c, $(OUT)src/%.o.ctr,$(src-y)) ./scripts/buildcommands.py
	@echo "  Building $@"
	$(Q)cat $(patsubst %.c, $(OUT)src/%.o.ctr,$(src-y)) | tr -s '\0' '\n' > $(OUT)compile_time_request.txt
	$(Q)$(PYTHON) ./scripts/buildcommands.py -d $(OUT)klipper.dict -t "$(CC);$(AS);$(LD);$(OBJCOPY);$(OBJDUMP);$(STRIP)" $(OUT)compile_time_request.txt $(OUT)compile_time_request.c
	$(Q)$(CC) $(CFLAGS) -c $(OUT)compile_time_request.c -o $@

################ 创建对应源文件的软链接

create-board-link:
	@echo "  Creating symbolic link $(OUT)board"
# 在out目录下创建对应的源码目录
	$(Q)mkdir -p $(addprefix $(OUT), $(dirs-y))
	$(Q)rm -f $(OUT)*.d $(patsubst %,$(OUT)%/*.d,$(dirs-y))
	$(Q)rm -f $(OUT)board
# 将out/board目录软链接到src/$(CONFIG_BOARD_DIRECTORY)目录
	$(Q)ln -sf $(CURDIR)/src/$(CONFIG_BOARD_DIRECTORY) $(OUT)board
# 将out/board-generic/board目录软链接到src/generic目录
	$(Q)mkdir -p $(OUT)board-generic
	$(Q)rm -f $(OUT)board-generic/board
	$(Q)ln -sf $(CURDIR)/src/generic $(OUT)board-generic/board

# Hack to rebuild OUT directory and reload make dependencies on Kconfig change
$(OUT)board-link: $(KCONFIG_CONFIG)
	@echo " Creating $@"
	$(Q)mkdir -p $(OUT)
	$(Q)echo "# Makefile board-link rule" > $@
	$(Q)$(MAKE) create-board-link

include $(OUT)board-link

################ Kconfig rules

# 通过.config配置文件生成autoconf.h头文件，该文件会在其他.o文件中被引用
$(OUT)autoconf.h: $(KCONFIG_CONFIG)
	@echo "  Building $@"
	$(Q)mkdir -p $(OUT)
	$(Q) KCONFIG_AUTOHEADER=$@ $(PYTHON) lib/kconfiglib/genconfig.py src/Kconfig

$(KCONFIG_CONFIG) olddefconfig: src/Kconfig
	$(Q)$(PYTHON) lib/kconfiglib/olddefconfig.py src/Kconfig

# 通过Kconfig生成.config配置文件
menuconfig:
	$(Q)$(PYTHON) lib/kconfiglib/menuconfig.py src/Kconfig

################ Generic rules

# Make definitions
.PHONY : all clean distclean olddefconfig menuconfig create-board-link FORCE
.DELETE_ON_ERROR:

all: $(target-y)

clean:
	$(Q)rm -rf $(OUT)

distclean: clean
	$(Q)rm -f .config .config.old

-include $(OUT)*.d $(patsubst %,$(OUT)%/*.d,$(dirs-y))
